```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redis消息模型详解 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', 'Noto Serif SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            line-height: 1.8;
            color: #333;
        }
        .hero {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        .code-block {
            background-color: #282c34;
            border-radius: 8px;
            font-family: 'Courier New', Courier, monospace;
        }
        .card-hover:hover {
            transform: translateY(-5px);
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
        }
        .transition-all {
            transition: all 0.3s ease;
        }
        .first-letter::first-letter {
            font-size: 2.5em;
            font-weight: bold;
            float: left;
            line-height: 0.8;
            margin-right: 8px;
            color: #667eea;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4">
        <div class="container mx-auto max-w-5xl">
            <div class="flex flex-col items-center text-center">
                <h1 class="text-4xl md:text-5xl font-bold mb-6">Redis消息模型详解</h1>
                <p class="text-xl md:text-2xl max-w-3xl mb-8 opacity-90">探索Redis作为消息队列的两种核心实现模式</p>
                <div class="flex space-x-4">
                    <a href="#publish-subscribe" class="px-6 py-3 bg-white text-indigo-600 font-medium rounded-lg hover:bg-gray-100 transition-all">
                        <i class="fas fa-broadcast-tower mr-2"></i>发布订阅模式
                    </a>
                    <a href="#producer-consumer" class="px-6 py-3 bg-indigo-700 text-white font-medium rounded-lg hover:bg-indigo-800 transition-all">
                        <i class="fas fa-exchange-alt mr-2"></i>生产者消费者模式
                    </a>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <main class="container mx-auto max-w-5xl px-4 py-12">
        <!-- Introduction -->
        <section class="mb-16">
            <div class="first-letter mb-8 text-gray-700 text-lg leading-relaxed">
                <p>Redis不仅是高性能的键值存储系统，还提供了强大的消息队列功能。在现代分布式系统中，消息队列是实现异步通信和解耦的重要组件。Redis通过其列表类型和发布/订阅功能，原生支持两种经典的消息模型：生产者消费者模式和发布者订阅者模式。</p>
            </div>
            
            <!-- Visualization -->
            <div class="bg-white rounded-xl shadow-lg p-6 mb-12 transition-all card-hover">
                <h2 class="text-2xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-project-diagram mr-3"></i>消息模型对比
                </h2>
                <div class="mermaid">
                    graph TD
                        subgraph 生产者消费者模式
                            P1[生产者] -->|消息| Q[(队列)]
                            Q --> C1[消费者1]
                            Q --> C2[消费者2]
                        end
                        subgraph 发布订阅模式
                            P2[发布者] -->|消息| T[主题]
                            T --> S1[订阅者1]
                            T --> S2[订阅者2]
                            T --> S3[订阅者3]
                        end
                </div>
                <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div class="bg-indigo-50 p-4 rounded-lg">
                        <h3 class="text-lg font-semibold mb-2 text-indigo-700">生产者消费者模式</h3>
                        <ul class="list-disc pl-5 space-y-1 text-gray-600">
                            <li>消息只能被一个消费者处理</li>
                            <li>适合任务分发场景</li>
                            <li>基于Redis列表实现</li>
                        </ul>
                    </div>
                    <div class="bg-purple-50 p-4 rounded-lg">
                        <h3 class="text-lg font-semibold mb-2 text-purple-700">发布订阅模式</h3>
                        <ul class="list-disc pl-5 space-y-1 text-gray-600">
                            <li>消息广播给所有订阅者</li>
                            <li>适合事件通知场景</li>
                            <li>基于Redis发布订阅功能</li>
                        </ul>
                    </div>
                </div>
            </div>
        </section>

        <!-- Publish Subscribe Section -->
        <section id="publish-subscribe" class="mb-16">
            <div class="flex items-center mb-8">
                <div class="bg-indigo-100 p-3 rounded-full mr-4">
                    <i class="fas fa-broadcast-tower text-indigo-600 text-2xl"></i>
                </div>
                <h2 class="text-3xl font-bold text-gray-800">发布订阅模型</h2>
            </div>
            
            <div class="bg-white rounded-xl shadow-lg p-6 mb-8 transition-all card-hover">
                <h3 class="text-xl font-semibold mb-4 text-indigo-700">工作原理</h3>
                <p class="text-gray-600 mb-4">发布订阅模式允许消息生产者（发布者）向特定频道发送消息，所有订阅了该频道的消费者（订阅者）都会接收到相同的消息副本。这种模式适用于需要广播消息的场景。</p>
                
                <div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
                    <div class="flex">
                        <div class="flex-shrink-0">
                            <i class="fas fa-info-circle text-blue-500 mt-1"></i>
                        </div>
                        <div class="ml-3">
                            <p class="text-sm text-blue-700">关键点：如果没有订阅者，发布的消息将会丢失。Redis的发布订阅模式不提供消息持久化功能。</p>
                        </div>
                    </div>
                </div>
                
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
                    <div class="code-block p-5 text-gray-200 overflow-x-auto">
                        <h4 class="text-white font-mono mb-3">1. 定义消费者</h4>
                        <pre><code>@Component
public class ConsumerRedisListener implements MessageListener {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        Object value = stringRedisTemplate.getValueSerializer()
            .deserialize(message.getBody());
        System.out.println("consumer message: " + 
            String.valueOf(value));
        System.out.println("pattern:"+pattern);
    }
}</code></pre>
                    </div>
                    <div class="code-block p-5 text-gray-200 overflow-x-auto">
                        <h4 class="text-white font-mono mb-3">2. 配置消费者</h4>
                        <pre><code>@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    
    @Autowired
    private ConsumerRedisListener consumerRedisListener;

    @Bean
    public ChannelTopic topic() {
        return new ChannelTopic("string-topic");
    }

    @Bean
    public RedisMessageListenerContainer container() {
        RedisMessageListenerContainer container = 
            new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener(consumerRedisListener, topic());
        return container;
    }
}</code></pre>
                    </div>
                </div>
                
                <div class="code-block p-5 text-gray-200 overflow-x-auto">
                    <h4 class="text-white font-mono mb-3">3. 生产者</h4>
                    <pre><code>@GetMapping("/topic/send")
public void send(String msg) {
    stringRedisTemplate.convertAndSend("string-topic", msg);
}</code></pre>
                </div>
            </div>
        </section>

        <!-- Producer Consumer Section -->
        <section id="producer-consumer" class="mb-16">
            <div class="flex items-center mb-8">
                <div class="bg-purple-100 p-3 rounded-full mr-4">
                    <i class="fas fa-exchange-alt text-purple-600 text-2xl"></i>
                </div>
                <h2 class="text-3xl font-bold text-gray-800">生产者消费者模型</h2>
            </div>
            
            <div class="bg-white rounded-xl shadow-lg p-6 transition-all card-hover">
                <h3 class="text-xl font-semibold mb-4 text-purple-700">工作原理</h3>
                <p class="text-gray-600 mb-4">生产者消费者模式中，生产者将消息放入队列，多个消费者竞争获取消息，但每条消息只能被一个消费者处理。这种模式适合任务分发场景，可以实现工作负载的均衡分配。</p>
                
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
                    <div class="code-block p-5 text-gray-200 overflow-x-auto">
                        <h4 class="text-white font-mono mb-3">1. 消费者</h4>
                        <pre><code>@Component
public class ConsumerQueueListener implements Runnable {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                String msg = stringRedisTemplate.opsForList()
                    .rightPop("queue-demo");
                System.out.println("接收到了:" + msg);
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }
}</code></pre>
                    </div>
                    <div class="code-block p-5 text-gray-200 overflow-x-auto">
                        <h4 class="text-white font-mono mb-3">2. 生产者</h4>
                        <pre><code>@GetMapping("/topic/send2")
public void send2(String msg) {
    stringRedisTemplate.opsForList()
        .leftPush("queue-demo", msg);
}</code></pre>
                    </div>
                </div>
                
                <div class="code-block p-5 text-gray-200 overflow-x-auto mb-6">
                    <h4 class="text-white font-mono mb-3">3. 启动线程</h4>
                    <pre><code>@GetMapping("/start")
public void start(){
    new Thread(consumerQueueListener).start();
}</code></pre>
                </div>
                
                <div class="bg-purple-50 border-l-4 border-purple-500 p-4">
                    <div class="flex">
                        <div class="flex-shrink-0">
                            <i class="fas fa-lightbulb text-purple-500 mt-1"></i>
                        </div>
                        <div class="ml-3">
                            <p class="text-sm text-purple-700">提示：可以使用BLPOP命令实现阻塞式获取消息，避免空轮询消耗资源。例如：rightPop("queue-demo", 5, TimeUnit.SECONDS)</p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Summary Section -->
        <section class="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-xl shadow-lg p-8 text-white">
            <h2 class="text-2xl font-bold mb-6 flex items-center">
                <i class="fas fa-clipboard-check mr-3"></i>模式选择指南
            </h2>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <h3 class="text-xl font-semibold mb-3">选择发布订阅模式当:</h3>
                    <ul class="list-disc pl-5 space-y-2">
                        <li>需要向多个接收者广播消息</li>
                        <li>消息实时性要求高</li>
                        <li>可以接受消息丢失(无订阅者时)</li>
                        <li>不需要保留历史消息</li>
                    </ul>
                </div>
                <div>
                    <h3 class="text-xl font-semibold mb-3">选择生产者消费者模式当:</h3>
                    <ul class="list-disc pl-5 space-y-2">
                        <li>每条消息只需被处理一次</li>
                        <li>需要消息持久化</li>
                        <li>需要工作负载均衡</li>
                        <li>可以接受消息处理的延迟</li>
                    </ul>
                </div>
            </div>
        </section>
    </main>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-8 px-4">
        <div class="container mx-auto max-w-5xl text-center">
            <div class="mb-4">
                <a href="http://www.yuque.com/jtostring" class="text-white hover:text-indigo-300 transition-colors">
                    技术小馆
                </a>
            </div>
            <div>
                <a href="http://www.yuque.com/jtostring" class="text-gray-400 hover:text-white transition-colors">
                    <i class="fas fa-globe mr-2"></i>http://www.yuque.com/jtostring
                </a>
            </div>
        </div>
    </footer>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>
```